package com.xxadmin.usercenter.config.security;

import com.xxadmin.usercenter.config.security.handler.CustomAuthExceptionEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import javax.annotation.Resource;
import javax.sql.DataSource;

@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Resource(name = "dataSource")
    private DataSource dataSource;

    @Autowired
    @Qualifier("customWebResponseExceptionTranslator")
    private WebResponseExceptionTranslator customWebResponseExceptionTranslator;

    /**
     * 将ClientDetailsServiceConfigurer（从您的回调AuthorizationServerConfigurer）可以用来在内存或JDBC实现客户的细节服务来定义的。客户端的重要属性是
     * clientId：（必填）客户端ID。
     * secret:(可信客户端需要）客户机密码（如果有）。没有可不填
     * scope：客户受限的范围。如果范围未定义或为空（默认值），客户端不受范围限制。read write all
     * authorizedGrantTypes：授予客户端使用授权的类型。默认值为空。
     * authorities授予客户的授权机构（普通的Spring Security权威机构）。
     * 客户端的详细信息可以通过直接访问底层商店（例如，在数据库表中JdbcClientDetailsService）或通过ClientDetailsManager接口（这两种实现ClientDetailsService也实现）来更新运行的应用程序。
     * 注意：JDBC服务的架构未与库一起打包（因为在实践中可能需要使用太多变体）
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();

        // 保存在服务器内存中，如果是单台应用其实是比较好选择，如果是集群的话，则不利于修改，调整
//        clients.inMemory()
//                .withClient("user-service")
//                .secret(passwordEncoder.encode("123456"))
//                .scopes("service")
//                .authorizedGrantTypes("client_credentials")
//                .accessTokenValiditySeconds(3600);

        clients.withClientDetails(clientDetails());
    }

    @Bean
    public ClientDetailsService clientDetails() {
        return new JdbcClientDetailsService(dataSource);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore())
                .tokenEnhancer(jwtTokenEnhancer())
                .authenticationManager(authenticationManager);

        // 扩展token返回结果--待补充

        // 自定义异常翻译器
        endpoints.exceptionTranslator(customWebResponseExceptionTranslator);
    }

    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtTokenEnhancer());
    }

    private JwtAccessTokenConverter jwtTokenEnhancer() {
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("cnsesan-jwt.jks"),"cnsesan123".toCharArray());
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setKeyPair(keyStoreKeyFactory.getKeyPair("cnsesan-jwt"));
        return converter;
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients();

        security.authenticationEntryPoint(new CustomAuthExceptionEntryPoint());
    }

}
